import { TraverseDeepOptions } from '../../common'
import { BoolUtil } from '../../bool'
import { traverseDeepRecursion } from './traverse-deep-recursion'

/**
 * 深度遍历对象
 *
 * @param {Record<any, any>} obj 需要深度遍历的对象
 * @param {TraverseDeepOptions} options 配置选项：
 * - `undefinedHandler`: 在遍历到数组或对象中的值为 undefined 时执行的函数，默认值 `() => {}`，
 * 函数接收两个参数：
 *   1. `item`: 当前遍历到数组或对象中的值，为 undefined
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 * - `nullHandler`: 在遍历到数组或对象中的值为 null 时执行的函数，默认值 `() => {}`，函数
 * 接收两个参数：
 *   1. `item`: 当前遍历到数组或对象中的值，为 null
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 * - `funHandler`: 在遍历到数组或对象中的值为函数时执行的函数，默认值 `() => {}`，函数接收
 * 两个参数：
 *   1. `item`: 当前遍历到数组或对象中的值，为函数
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 * - `excludeObjProps`: 对于目前正在遍历的对象类型的值，需要排除的对象属性的名称数组，如果不
 * 指定或数组为空，则不排除任何属性，如果相同的属性名同时出现在 excludeObjProps 和 includeObjProps
 * 中，则该属性将被排除（excludeObjProps 优先级更高），默认值 `[]`
 * - `includeObjProps`: 对于目前正在遍历的对象类型的值，需要包含的对象属性的名称数组，默认值
 * `[]`
 * - `objPreHandler`: 在遍历到对象类型的值时，对对象进行深度遍历之前执行的函数，默认值 `() => {}`，
 * 函数接收四个参数：
 *   1. `item`: 当前遍历到的对象类型的值
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `parent`: 当前遍历到的对象的父级
 *   4. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 * - `excludeArrItemHandler`: 对于目前正在遍历的数组类型的值，需要排除数组中的项的条件处理
 * 函数，如果该函数返回 true 则该项将被排除，默认值 `() => false`，函数接收一个参数：
 *   1. `item`: 当前遍历到的数组项
 * - `arrPreHandler`: 在遍历到数组类型的值时，对数组进行深度遍历之前执行的函数，默认值 `() => {}`，
 * 函数接收四个参数：
 *   1. `item`: 当前遍历到的数组类型的值
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `parent`: 当前遍历到的数组的父级
 *   4. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 * - `otherHandler`: 在遍历到数组或对象中的值的类型不在上述涉及到的类型中时，执行的函数，默认
 * 值 `() => {}`，函数接收两个参数：
 *   1. `item`: 当前遍历到的数组或对象中的值
 *   2. `key`：当前遍历到数组或对象中的值，在数组中的索引或对象中的属性名称
 *   3. `setStop`: 停止遍历的函数，调用该函数将停止遍历
 */
export const traverseDeep = (obj: Record<any, any>, options: TraverseDeepOptions = {}) => {
  const stop = BoolUtil.useBool()
  traverseDeepRecursion(obj, { ...options, getStop: stop.getter, setStop: stop.setTrue })
}

export type TraverseDeepType = typeof traverseDeep
